//! 该组件接收编辑器发送的所有文本相关事件，如字符按键、换行符等。
//! 它负责将这些事件转换为可视化的文本内容，并在终端上进行渲染。

mod buffer;
mod search;
use buffer::Buffer;
use search::{SearchDirection, SearchInfo};

use std::cmp::min;
use std::io::Error;

use crate::editor::Highlighter;
use crate::editor::command::{Edit, Move};
use crate::editor::line::Line;
use crate::editor::terminal::Terminal;
use crate::editor::ui::UI;
use crate::editor::ui::status::DocumentStatus;
use crate::prelude::*;

#[derive(Default)]
pub struct View {
    buffer: Buffer,
    needs_redraw: bool,
    // 视图总是从“（0/0）”开始。“size”属性决定了可见区域。
    size: Size,
    // 光标位于文本中的位置
    text_location: Location,
    // 将终端看作一个网格，scroll_offset表示网格上的行和列。
    scroll_offset: Position,
    search_info: Option<SearchInfo>,
}

impl View {
    pub fn get_document_status(&self) -> DocumentStatus {
        let file_info = self.buffer.get_file_info();

        DocumentStatus {
            total_lines: self.buffer.height(),
            caret_line_idx: self.text_location.line_idx,
            caret_cell_idx: self.text_location.cell_idx,
            is_modified: self.buffer.is_dirty(),
            file_info: file_info.clone(),
        }
    }

    // 允许 Editor 确定是否需要提示输入文件名。
    pub const fn is_file_loaded(&self) -> bool {
        self.buffer.is_file_loaded()
    }

    // 文件 io 操作 start

    // 进入搜索
    pub fn enter_search(&mut self) {
        self.search_info = Some(SearchInfo {
            prev_location: self.text_location,
            prev_scroll_offset: self.scroll_offset,
            query: None,
        });
    }

    // 退出搜索
    pub fn exit_search(&mut self) {
        self.search_info = None;
        self.set_needs_redraw(true);
    }

    // 取消搜索
    pub fn dismiss_search(&mut self) {
        if let Some(search_info) = &self.search_info {
            self.text_location = search_info.prev_location;
            self.scroll_offset = search_info.prev_scroll_offset;
            self.scroll_text_location_into_view();
        }
        self.exit_search();
    }

    // 搜索
    pub fn search(&mut self, query: &str) {
        if let Some(search_info) = &mut self.search_info {
            search_info.query = Some(Line::from(query));
        }

        self.search_in_direction(self.text_location, SearchDirection::Forward);
    }

    fn get_search_query(&self) -> Option<&Line> {
        let query = self
            .search_info
            .as_ref()
            .and_then(|search_info| search_info.query.as_ref());

        debug_assert!(
            query.is_some(),
            "Attempting to search with malformed searchinfo present"
        );
        query
    }

    fn search_in_direction(&mut self, from: Location, direction: SearchDirection) {
        if let Some(location) = self.get_search_query().and_then(|query| {
            if query.is_empty() {
                None
            } else if direction == SearchDirection::Forward {
                self.buffer.search_forward(query, from)
            } else {
                self.buffer.search_backward(query, from)
            }
        }) {
            self.text_location = location;
            self.center_text_location();
        };
        self.set_needs_redraw(true);
    }

    // 在当前位置的左侧寻找下一个出现的位置
    pub fn search_next(&mut self) {
        let step_right = self
            .get_search_query()
            .map_or(1, |query| min(query.cells_count(), 1));
        let location = Location {
            line_idx: self.text_location.line_idx,

            cell_idx: self.text_location.cell_idx.saturating_add(step_right),
        };

        self.search_in_direction(location, SearchDirection::Forward);
    }

    pub fn search_prev(&mut self) {
        self.search_in_direction(self.text_location, SearchDirection::Backward);
    }

    // 加载文本到缓冲区
    pub fn load(&mut self, file_name: &str) -> Result<(), Error> {
        let buffer = Buffer::load(file_name)?;
        self.buffer = buffer;
        self.set_needs_redraw(true);
        Ok(())
    }

    pub fn save(&mut self) -> Result<(), Error> {
        self.buffer.save()?;
        self.set_needs_redraw(true);
        Ok(())
    }

    pub fn save_as(&mut self, file_name: &str) -> Result<(), Error> {
        self.buffer.save_as(file_name)?;
        self.set_needs_redraw(true);
        Ok(())
    }

    // 文件 io 操作 end

    // 命令处理 start

    pub fn handle_edit_command(&mut self, command: Edit) {
        match command {
            Edit::Insert(char) => self.insert_char(char),
            Edit::InsertNewline => self.insert_newline(),
            Edit::Delete => self.delete(),
            Edit::DeleteBackward => self.delete_backspace(),
        }
    }

    pub fn handle_move_command(&mut self, command: Move) {
        let Size { height, .. } = self.size;

        match command {
            Move::Up => self.move_up(1),
            Move::Down => self.move_down(1),
            Move::Left => self.move_left(),
            Move::Right => self.move_right(),
            Move::StartOfLine => self.move_to_start_of_line(),
            Move::EndOfLine => self.move_to_end_of_line(),
            Move::PageUp => self.move_up(height.saturating_sub(1)),
            Move::PageDown => self.move_down(height.saturating_sub(1)),
        }
        // 滚动到该位置
        self.scroll_text_location_into_view();
    }

    // 命令处理 end

    // 文本编辑 start

    fn insert_newline(&mut self) {
        self.buffer.insert_newline(self.text_location);
        self.handle_move_command(Move::Right);
        self.set_needs_redraw(true);
    }

    fn delete_backspace(&mut self) {
        if self.text_location.line_idx != 0 || self.text_location.cell_idx != 0 {
            self.handle_move_command(Move::Left);
            self.delete();
        }
    }

    fn delete(&mut self) {
        self.buffer.delete(self.text_location);
        self.set_needs_redraw(true);
    }

    fn insert_char(&mut self, character: char) {
        let old_len = self.buffer.grapheme_count(self.text_location.line_idx);

        self.buffer.insert_char(character, self.text_location);

        let new_len = self.buffer.grapheme_count(self.text_location.line_idx);

        let grapheme_delta = new_len.saturating_sub(old_len);
        if grapheme_delta > 0 {
            // 向右移动以添加字形（常规情况）
            self.handle_move_command(Move::Right);
        }

        self.set_needs_redraw(true);
    }

    // 文本编辑 end

    // 渲染逻辑 start

    fn render_line(at: RowIdx, line_text: &str) -> Result<(), Error> {
        Terminal::print_row(at, line_text)
    }

    fn build_welcome_message(width: usize) -> String {
        if width == 0 {
            return String::new();
        }

        let welcome_message = format!("{NAME} -- version {VERSION}");
        let len = welcome_message.len();

        let remaining_width = width.saturating_sub(1);
        if remaining_width < len {
            return "~".to_string();
        }

        format!("{:<1}{:^remaining_width$}", "~", welcome_message)
    }

    // 渲染逻辑 end

    // 滚动 start

    // 纵向滚动，滚动到指定行
    fn scroll_vertically(&mut self, to: RowIdx) {
        let Size { height, .. } = self.size;
        let offset_changed = if to < self.scroll_offset.row {
            self.scroll_offset.row = to;
            true
        } else if to >= self.scroll_offset.row.saturating_add(height) {
            self.scroll_offset.row = to.saturating_sub(height).saturating_add(1);
            true
        } else {
            false
        };

        if offset_changed {
            self.set_needs_redraw(true);
        }
    }

    // 横向滚动，滚动到指定列
    fn scroll_horizontally(&mut self, to: ColIdx) {
        let Size { width, .. } = self.size;
        let offset_changed = if to < self.scroll_offset.col {
            self.scroll_offset.col = to;
            true
        } else if to >= self.scroll_offset.col.saturating_add(width) {
            self.scroll_offset.col = to.saturating_sub(width).saturating_add(1);
            true
        } else {
            false
        };

        if offset_changed {
            self.set_needs_redraw(true);
        }
    }

    fn scroll_text_location_into_view(&mut self) {
        // 将文本位置转换为网格上的位置，并滚动到该位置
        let Position { row, col } = self.text_location_to_position();
        self.scroll_vertically(row);
        self.scroll_horizontally(col);
    }

    fn center_text_location(&mut self) {
        let Size { height, width } = self.size;
        let Position { row, col } = self.text_location_to_position();
        let vertical_mid = height.div_ceil(2);
        let horizontal_mid = width.div_ceil(2);

        self.scroll_offset.row = row.saturating_sub(vertical_mid);
        self.scroll_offset.col = col.saturating_sub(horizontal_mid);
        self.set_needs_redraw(true);
    }

    // 滚动 end

    // Location 和 Position 的处理 start

    // 获取当前光标位置
    pub fn caret_position(&self) -> Position {
        self.text_location_to_position()
            .saturating_sub(&self.scroll_offset)
    }

    // 将文本的位置转换为一个 1x1 单元格网格中的位置。
    fn text_location_to_position(&self) -> Position {
        let row = self.text_location.line_idx;
        debug_assert!(row.saturating_sub(1) <= self.buffer.height());
        let col = self.buffer.width_until(row, self.text_location.cell_idx);
        Position { col, row }
    }

    // Location 和 Position 的处理 end

    // 按照指令移动光标 start

    fn move_up(&mut self, step: usize) {
        self.text_location.line_idx = self.text_location.line_idx.saturating_sub(step);
        self.snap_to_valid_grapheme();
    }

    fn move_down(&mut self, step: usize) {
        self.text_location.line_idx = self.text_location.line_idx.saturating_add(step);
        self.snap_to_valid_grapheme();
        self.snap_to_valid_line();
    }

    fn move_left(&mut self) {
        if self.text_location.cell_idx > 0 {
            self.text_location.cell_idx -= 1;
        } else if self.text_location.line_idx > 0 {
            self.move_up(1);
            self.move_to_end_of_line();
        }
    }

    fn move_right(&mut self) {
        let grapheme_count = self.buffer.grapheme_count(self.text_location.line_idx);

        if self.text_location.cell_idx < grapheme_count {
            self.text_location.cell_idx += 1;
        } else {
            self.move_to_start_of_line();
            self.move_down(1);
        }
    }

    fn move_to_start_of_line(&mut self) {
        self.text_location.cell_idx = 0;
    }

    fn move_to_end_of_line(&mut self) {
        self.text_location.cell_idx = self.buffer.grapheme_count(self.text_location.line_idx);
    }

    fn snap_to_valid_grapheme(&mut self) {
        self.text_location.cell_idx = min(
            self.text_location.cell_idx,
            self.buffer.grapheme_count(self.text_location.line_idx),
        );
    }

    fn snap_to_valid_line(&mut self) {
        self.text_location.line_idx = min(self.text_location.line_idx, self.buffer.height());
    }

    // 按照指令移动光标到文本指定位置 end
}

impl UI for View {
    fn set_needs_redraw(&mut self, value: bool) {
        self.needs_redraw = value;
    }

    fn needs_redraw(&self) -> bool {
        self.needs_redraw
    }

    fn set_size(&mut self, size: Size) {
        self.size = size;
        self.scroll_text_location_into_view();
    }

    fn draw(&mut self, origin_row: RowIdx) -> Result<(), std::io::Error> {
        let Size { height, width } = self.size;
        let end_y = origin_row.saturating_add(height);
        let top_third = height.div_ceil(3);
        let scroll_top = self.scroll_offset.row;

        let query = self
            .search_info
            .as_ref()
            .and_then(|search_info| search_info.query.as_deref());

        let selected_match = query.is_some().then_some(self.text_location);

        let mut highlighter = Highlighter::new(
            query,
            selected_match,
            self.buffer.get_file_info().get_file_type(),
        );

        for current_row in 0..end_y.saturating_add(scroll_top) {
            self.buffer.highlight(current_row, &mut highlighter);
        }

        for current_row in origin_row..end_y {
            let line_idx = current_row
                .saturating_sub(origin_row)
                .saturating_add(scroll_top);

            let left = self.scroll_offset.col;
            let right = self.scroll_offset.col.saturating_add(width);

            if let Some(annotated_string) =
                self.buffer
                    .get_highlighted_substring(line_idx, left..right, &highlighter)
            {
                Terminal::print_annotated_row(current_row, &annotated_string)?;
            } else if current_row == top_third && self.buffer.is_empty() {
                Self::render_line(current_row, &Self::build_welcome_message(width))?;
            } else {
                Self::render_line(current_row, "~")?;
            }
        }

        Ok(())
    }
}
